package com.flying.client.network;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;

import com.flying.client.network.msg.OWMsg;
import com.flying.client.utils.Logger;

public class NetworkClient {
	private static final Logger logger = Logger.getLogger(NetworkClient.class.getName());
	
	static
	{
		System.setProperty("java.net.preferIPv4Stack", "true");   
        System.setProperty("java.net.preferIPv6Addresses", "false");
	}
	/**
	 * 服务IP
	 */
	private String serverIp;
	/**
	 * 服务端口
	 */
	private int serverPort;
	/**
	 * Socket通道
	 */
	private Channel channel;
	/**
	 * 网络引导驱动
	 */
	private ClientBootstrap bootstrap;
	/**
	 * 网络提供对象
	 */
	private NetworkProvider networkProvider = null;
	/**
	 * 网络连接线程
	 */
	private Thread connThreader = null;
	
	public NetworkClient(NetworkProvider networkProvider, String serverIp, int serverPort)
	{
		this.networkProvider = networkProvider;
		this.serverIp = serverIp;
		this.serverPort = serverPort;
	}
	
	public String serverIp()
	{
		return serverIp;
	}
	
	public int serverPort()
	{
		return serverPort;
	}
	/**
	 * 初始化
	 */
	public void init()
	{
		if (bootstrap != null && channel != null && channel.isConnected())
			return;
		
		logger.debug("connect serverIP="+serverIp + " serverPort="+serverPort);
		
		ChannelFactory factory = new OioClientSocketChannelFactory(Executors.newCachedThreadPool());
		
		bootstrap = new ClientBootstrap(factory);
		
		bootstrap.setPipelineFactory(new ChannelPipelineFactory()  
        {  
            @Override  
            public ChannelPipeline getPipeline() throws Exception  
            {  
                ChannelPipeline pipeline =Channels.pipeline();  
                
                pipeline.addLast("decoder", new MsgDecoder());  
                pipeline.addLast("encoder", new MsgEncoder());
                pipeline.addLast("handler", new MsgEventHandler(networkProvider));
                pipeline.addLast("dispatch", new MsgDispatchHandler(networkProvider));
  
                return pipeline;  
            }
        });
		
		bootstrap.setOption("tcpNoDelay", true);   
        bootstrap.setOption("keepAlive", true);   
        bootstrap.setOption("reuseAddress", true);   
        bootstrap.setOption("connectTimeoutMillis", "10000");
	}
	/**
	 * 网络连接
	 * @return
	 */
	public void asyncConnect(IAsyncSink sink)
	{
		if (bootstrap == null || channel != null || channel.isConnected())
			return;
		
		try
		{
			if (connThreader != null && !connThreader.isInterrupted() && connThreader.isAlive())
			{
				connThreader.interrupt();
			}
		}
		catch(Exception e)
		{
		}
		
		connThreader = new Thread(new ConnWorker(sink));
		connThreader.start();
	}
	public boolean connect()
	{
		logger.debug("conn bootstrap="+bootstrap);
		
		if (bootstrap == null || channel != null || channel.isConnected())
			return false;
		
	    ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress(this.serverIp,this.serverPort));
        
        channel = channelFuture.getChannel();
		channel.setAttachment(new NetworkAttachment(NetworkClient.this,networkProvider));
		
		logger.debug("setted attachment");
        channelFuture.addListener(new ChannelFutureListener(){
			@Override
			public void operationComplete(ChannelFuture future)
					throws Exception {
				
				//channel = future.getChannel();
				//channel.setAttachment(new NetworkAttachment(NetworkClient.this,networkProvider));
				
				if (future.isSuccess())
				{
					channel = future.getChannel();
					channel.setAttachment(new NetworkAttachment(NetworkClient.this,networkProvider));
					logger.debug("connect success channelFuture="+future.isCancelled() + " "+future.isDone()+" "+future.isSuccess() + " "+channel.isConnected());
				}
				else
				{
					logger.debug("connect failed");
				}
			}
        });
        
        return true;
	}
	/**
	 * 关闭网络
	 * @return
	 */
	public boolean close()
	{
		if (channel == null)
			return false;
		
		ChannelFuture future = channel.close();
		channel.getFactory().releaseExternalResources();
		return future.isSuccess();
	}
	/**
	 * 是否有效
	 * @return
	 */
	public boolean isValid()
	{
		return channel != null && channel.isConnected();
	}
	/**
	 * 是否可读
	 * @return
	 */
	public boolean canWrite()
	{
		return channel != null && channel.isWritable();
	}
	/**
	 * 是否可写
	 * @return
	 */
	public boolean canRead()
	{
		return channel != null && channel.isReadable();
	}
	/**
	 * 发送
	 * @param msg
	 * @return
	 */
	public boolean send(OWMsg msg)
	{
		if (msg == null)
			return false;
		
		if (!isValid() || !canWrite())
		{
			msg.onError();
			return false;
		}
		
		ChannelFuture future = channel.write(msg);
		logger.debug("send future.isSuccess="+future.isSuccess());
		
		if (future.isSuccess())
		{
			msg.onSended();
		}
		else
		{
			msg.onError();
		}
		
		return future.isSuccess();
	}
	
	class ConnWorker implements Runnable
	{
		private IAsyncSink sink;
		ConnWorker(IAsyncSink sink)
		{
			this.sink = sink;
		}
		@Override
		public void run() {
			
			if (networkProvider.canWrite())
				return;
			
			boolean conned = connect();
			
			if (sink != null)
			{
				sink.onResult(conned,NetworkClient.this);
			}
		}		
	}
	
	public interface IAsyncSink
	{
		void onResult(boolean conned,NetworkClient client);
	}
}
